package io.github.talelin.latticy.util;

/**
 * @authoer:wulongbo
 * @createDate:2022/6/6
 * @description:
 */

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class  GenericsUtils {

    /**
     * 获取接口上的泛型T
     *
     * @param o     接口
     */
    public static Class<?> getSuperInterfaceT(Object o) {
        return getInterfaceT(o, 0);
    }

    /**
     * 获取接口上的泛型T
     *
     * @param o     接口
     * @param index 泛型索引
     */
    public static Class<?> getInterfaceT(Object o, int index) {
        Type[] types = o.getClass().getGenericInterfaces();
        ParameterizedType parameterizedType = (ParameterizedType) types[index];
        Type type = parameterizedType.getActualTypeArguments()[index];
        return checkType(type, index);

    }

    /**
     * 获取类上的泛型T
     *
     * @param o     接口
     * @param index 泛型索引
     */
    public static Class<?> getClassT(Object o, int index) {
        Type type = o.getClass().getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type actType = parameterizedType.getActualTypeArguments()[index];
            return checkType(actType, index);
        } else {
            String className = type == null ? "null" : type.getClass().getName();
            throw new IllegalArgumentException("Expected a Class, ParameterizedType"
                    + ", but <" + type + "> is of type " + className);
        }
    }

    private static Class<?> checkType(Type type, int index) {
        if (type instanceof Class<?>) {
            return (Class<?>) type;
        } else if (type instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) type;
            Type t = pt.getActualTypeArguments()[index];
            return checkType(t, index);
        } else {
            String className = type == null ? "null" : type.getClass().getName();
            throw new IllegalArgumentException("Expected a Class, ParameterizedType"
                    + ", but <" + type + "> is of type " + className);
        }
    }

    /**
     * 通过反射,获得定义Class时声明的父类的范型参数的类型. 如public BookManager extends
     * GenricManager<Book>
     *
     * @param clazz The class to introspect
     * @return the first generic declaration, or <code>Object.class</code> if cannot be determined
     */
    public static Class getSuperClassGenricType(Class clazz) {
        return getSuperClassGenricType(clazz, 0);
    }

    /**
     * 通过反射,获得定义Class时声明的父类的范型参数的类型. 如public BookManager extends GenricManager<Book>
     *
     * @param clazz clazz The class to introspect
     * @param index the Index of the generic ddeclaration,start from 0.
     */
    public static Class getSuperClassGenricType(Class clazz, int index)
            throws IndexOutOfBoundsException {
        Type genType = clazz.getGenericSuperclass();
        if (!(genType instanceof ParameterizedType)) {
            return Object.class;
        }
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        if (index >= params.length || index < 0) {
            return Object.class;
        }
        if (!(params[index] instanceof Class)) {
            return Object.class;
        }
        return (Class) params[index];
    }

    //获取第二个泛型类型
    public static <T> Class<T> getSuperGenericClass2(Class<?> clz) {
        Class<T> result = null;
        //得到当前对象的父类"泛型类型"(也叫参数化类型)
        //superclass == GenericDao<Dept>成为参数化类型
        //superclass == BaseDao不是参数化类型
        Type superclass = clz.getGenericSuperclass();
        //判断类型是否为参数化类型
        if (superclass instanceof ParameterizedType) {
            //把父类类型转换成参数化类型（泛型类型）
            //只有ParameterizedType才能通过getActualTypeArguments得到参数
            ParameterizedType parameterizedType = (ParameterizedType) superclass;
            //得到参数化类型类型的参数
            //types == GenericDao<Dept>的"<Dept>"参数
            Type[] types = parameterizedType.getActualTypeArguments();
            //返回子类传递的类型
            result = (Class<T>) types[1];
        }
        return result;
    }
}

